import subprocess
import threading
import time

class FFmpegPusher:
    def __init__(self, ffmpeg_path, rtmp_url, log_callback=None):
        self.ffmpeg_path = ffmpeg_path
        self.rtmp_url = rtmp_url
        self.push_process = None
        self.push_thread = None
        self.is_pushing = False
        self.start_time = 0
        self.log_callback = log_callback
    
    def update_config(self, ffmpeg_path=None, rtmp_url=None):
        """更新配置"""
        if ffmpeg_path:
            self.ffmpeg_path = ffmpeg_path
        if rtmp_url:
            self.rtmp_url = rtmp_url
    
    def start_device_push(self, video_device, audio_device=None):
        """开始设备推流"""
        # 构建输入设备字符串 - 注意这里需要处理引号，因为我们会将整个命令作为字符串传递
        if audio_device:
            # 视频和音频合并为一个输入源
            if ' ' in video_device or ' ' in audio_device:
                input_device = f'video="{video_device}":audio="{audio_device}"'
            else:
                input_device = f'video={video_device}:audio={audio_device}'
        else:
            # 只有视频设备
            if ' ' in video_device:
                input_device = f'video="{video_device}"'
            else:
                input_device = f'video={video_device}'
        
        # 构建完整的FFmpeg命令字符串，模拟在CMD中执行的方式
        cmd_str = f'"{self.ffmpeg_path}" -f dshow -i {input_device} -vcodec libx264 -preset veryfast -tune zerolatency -b:v 2000k -maxrate 2500k -bufsize 2000k -pix_fmt yuv420p -g 50'
        
        # 添加音频编码参数（如果有音频设备）
        if audio_device:
            cmd_str += ' -acodec aac -b:a 128k -ar 44100 -ac 2 -shortest'
        
        # RTMP输出设置
        cmd_str += f' -f flv {self.rtmp_url}'
        
        return self.start_push(cmd_str, "设备推流")
    
    def start_window_push(self, window_title, audio_device=None, fps="30"):
        """开始窗口推流"""
        # 构建FFmpeg命令 - 使用窗口标题而不是固定坐标，添加更多参数以提高兼容性
        cmd = [
            self.ffmpeg_path,
            "-f", "gdigrab",
            "-framerate", fps,
            "-show_region", "1",
            "-draw_mouse", "1",
            "-probesize", "42M",
            "-i", f"title=\"{window_title}\"",
        ]
        
        # 添加音频捕获
        if audio_device:
            audio_input = f'audio="{audio_device}"' if ' ' in audio_device else f'audio={audio_device}'
            cmd.extend([
                "-f", "dshow",
                "-i", audio_input,
                "-c:a", "aac"
            ])
        
        # RTMP输出设置
        cmd.extend([
            "-f", "flv",
            "-flvflags", "no_duration_filesize",
            self.rtmp_url
        ])
        
        return self.start_push(cmd, "窗口推流")
    
    def start_window_push_dshow(self, window_title, audio_device=None, fps="30"):
        """使用dshow方式开始窗口推流（支持screen-capture-recorder虚拟设备）"""
        # 使用screen-capture-recorder虚拟设备录制窗口
        # 这是专门为了解决某些应用（如微信）窗口录制黑屏问题的方案
        
        # 构建FFmpeg命令参数列表，使用这种方式可以更好地处理引号和特殊字符
        cmd = [
            self.ffmpeg_path,
            "-f", "dshow",
            "-video_size", "1920x1080",
            "-i", f"video=screen-capture-recorder:title={window_title}"
        ]
        
        # 添加音频捕获（使用dshow）
        if audio_device:
            audio_input = f'audio="{audio_device}"' if ' ' in audio_device else f'audio={audio_device}'
            cmd.extend(["-f", "dshow", "-i", audio_input])
        
        # 编码和输出设置
        cmd.extend([
            "-vcodec", "libx264",
            "-preset", "veryfast",
            "-tune", "zerolatency",
            "-b:v", "2000k",
            "-maxrate", "2500k",
            "-bufsize", "2000k",
            "-pix_fmt", "yuv420p",
            "-g", "50"
        ])
        
        if audio_device:
            cmd.extend([
                "-acodec", "aac",
                "-b:a", "128k",
                "-ar", "44100",
                "-ac", "2",
                "-shortest"
            ])
        
        # RTMP输出
        cmd.extend([
            "-f", "flv",
            "-flvflags", "no_duration_filesize",
            self.rtmp_url
        ])
        
        # 构建命令字符串用于日志
        cmd_str = '"' + '" "'.join(cmd) + '"' if ' ' in ''.join(cmd) else ' '.join(cmd)
        
        return self.start_push(cmd, "窗口推流(screen-capture-recorder)")
    
    def start_screen_push(self, fps="30", audio_device=None, screen_index="0"):
        """开始全屏录制"""
        # 构建FFmpeg命令
        cmd = [
            self.ffmpeg_path,
            "-f", "gdigrab",
            "-framerate", fps,
        ]
        
        # 如果指定了显示器索引，添加monitor_index参数
        if screen_index != "0":
            cmd.extend(["-monitor_index", screen_index])
            
        cmd.extend(["-i", "desktop"])
        
        # 添加音频捕获
        if audio_device:
            audio_input = f'audio="{audio_device}"' if ' ' in audio_device else f'audio={audio_device}'
            cmd.extend([
                "-f", "dshow",
                "-i", audio_input,
                "-c:a", "aac"
            ])
        
        # RTMP输出设置
        cmd.extend([
            "-f", "flv",
            "-flvflags", "no_duration_filesize",
            self.rtmp_url
        ])
        
        return self.start_push(cmd, f"全屏录制 - 显示器{screen_index} - {fps}fps")
    
    def start_region_push(self, left, top, width, height, fps="30", audio_device=None):
        """开始区域录制"""
        # 构建FFmpeg命令
        cmd = [
            self.ffmpeg_path,
            "-f", "gdigrab",
            "-framerate", fps,
            "-offset_x", str(left),
            "-offset_y", str(top),
            "-video_size", f"{width}x{height}",
            "-i", "desktop",
        ]
        
        # 添加音频捕获
        if audio_device:
            audio_input = f'audio="{audio_device}"' if ' ' in audio_device else f'audio={audio_device}'
            cmd.extend([
                "-f", "dshow",
                "-i", audio_input,
                "-c:a", "aac"
            ])
        
        # RTMP输出设置
        cmd.extend([
            "-f", "flv",
            "-flvflags", "no_duration_filesize",
            self.rtmp_url
        ])
        
        return self.start_push(cmd, f"区域录制 - {width}x{height} - {fps}fps")
    
    def start_push(self, cmd, title):
        """启动推流过程"""
        self._log(f"开始{title}...")
        # 检查cmd类型并处理
        is_cmd_list = isinstance(cmd, list)
        cmd_str = ' '.join(cmd) if is_cmd_list else cmd
        self._log(f"执行命令: {cmd_str}")
        
        try:
            # 根据命令类型选择不同的启动方式
            if is_cmd_list:
                self.push_process = subprocess.Popen(
                    cmd,  # 直接传递参数列表
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                    shell=False
                )
            else:
                self.push_process = subprocess.Popen(
                    cmd,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                    shell=True
                )
            
            self.is_pushing = True
            self.start_time = time.time()
            
            # 启动线程读取输出
            # self.push_thread = threading.Thread(target=self._read_ffmpeg_output)
            # self.push_thread.daemon = True
            # self.push_thread.start()
            
            return True
            
        except Exception as e:
            self._log(f"启动推流失败: {str(e)}")
            import traceback
            self._log(f"异常堆栈: {traceback.format_exc()}")
            self.is_pushing = False
            return False
    
    def _read_ffmpeg_output(self):
        """读取FFmpeg输出"""
        while self.is_pushing and self.push_process.poll() is None:
            try:
                # 读取错误输出 - 增加更好的异常处理
                if self.push_process and self.push_process.stderr:
                    # 使用非阻塞读取方式
                    try:
                        # 尝试读取一行
                        line = self.push_process.stderr.readline()
                        if line:
                            self._log(line.strip())
                    except Exception as e:
                        # 捕获读取错误但不中断循环
                        pass
            except Exception as e:
                # 记录任何异常但继续运行
                self._log(f"读取输出异常: {str(e)}")
            
            time.sleep(0.1)
        
        # 检查进程状态
        if self.push_process.poll() is not None and self.is_pushing:
            self._log(f"FFmpeg进程已结束，退出码: {self.push_process.returncode}")
            self.stop_push()
    
    def stop_push(self):
        """停止推流"""
        self._log("停止推流...")
        
        # 终止FFmpeg进程
        if self.push_process and self.push_process.poll() is None:
            try:
                # 尝试优雅终止
                self.push_process.terminate()
                # 等待进程终止
                self.push_process.wait(timeout=5)
            except:
                # 强制终止
                if hasattr(self.push_process, 'kill'):
                    self.push_process.kill()
        
        # 更新状态
        self.is_pushing = False
        self._log("推流已停止")
    
    def _log(self, message):
        """记录日志"""
        if self.log_callback:
            self.log_callback(message)
        else:
            print(message)
    
    def get_elapsed_time(self):
        """获取推流时长"""
        if self.is_pushing:
            return int(time.time() - self.start_time)
        return 0
    
    def is_streaming(self):
        """检查是否正在推流"""
        return self.is_pushing